/* 
 * File:   mrf_segmentation.cpp
 * Author: hadrien
 *
 * Created on 19 avril 2012, 03:21
 */

#include <cstdlib>
#include <cv.h>
#include <cvaux.h>
#include <highgui.h>
#include <stdio.h>
#include <iostream>
#include "Gibbs.h"
#include "GaussianProcess.h"
#include "GaussianMRFEnergyDoubleton.h"
#include "cvgabor.h"
#include "MRFSegmentation.h"
#include "GaborUtility.h"

using namespace cv;
using namespace std;

class ImageParameters {
public:
    typedef Mat_<double> features_vector_t;
    typedef int label_t;
    static int _nbFeature;
    static int _height;
    static int _width;
    static int _numberClass;
    static double _beta;

    inline static int getL(void) {
        return _numberClass;
    }//Number of classes

    inline static int getHeight(void) {
        return _height;
    } //Image size

    inline static int getWidth(void) {
        return _width;
    } //Image size    

    inline static int getN(void) {
        return _nbFeature;
    } //Number of features

    inline static double getBeta(void) {
        return _beta;
    } //Homogeneity
};
int ImageParameters::_nbFeature;
int ImageParameters::_height;
int ImageParameters::_width;
int ImageParameters::_numberClass;
double ImageParameters::_beta;

class GibbsParameters {
public:

    static double _initial_temperature;
    static double _decreaseTemperature;
    static double _stop_criterion;

    inline static double getDecreaseTemperature() {
        return _decreaseTemperature;
    }//Number of classes

    inline static double getStopCriterion() {
        return _stop_criterion;
    } //Stop criterion

    inline static double getInitialTemperature() {
        return _initial_temperature;
    } //Image size
};
double GibbsParameters::_initial_temperature;
double GibbsParameters::_decreaseTemperature;
double GibbsParameters::_stop_criterion;

class EMParameters {
public:

    static double _maxIter;
    static double _stop_criterion;

    inline static double getStopCriterion() {
        return _stop_criterion;
    } //Stop criterion

    inline static double getMaxIterations() {
        return _maxIter;
    } //Image size
};
double EMParameters::_maxIter;
double EMParameters::_stop_criterion;


typedef GaussianProcess<ImageParameters> model_t;
typedef GaussianMRFEnergyDoubleton<ImageParameters> energy_t;
typedef Gibbs<energy_t, GibbsParameters> optimization_t;
typedef MRFSegmentation<optimization_t, EMParameters> mrf_t;

typedef typename ImageParameters::features_vector_t features_vector_t;
typedef typename ImageParameters::label_t label_t;

/*
 * 
 */
int main(int argc, char** argv) {
    //Random generator init
    srand((unsigned) time(0));
    //Loading image
    IplImage* img = cvLoadImage(argv[1]);

    if (!img) {
        printf("Could not load %s\n", argv[1]);
        exit(0);
    }

    printf("Width: %d\nHeight: %d\n", img->width, img->height);
    //Compute feature
    features_vector_t** obs = GaborUtility::computeFeatures(img);
    printf("Features computed\n");
    
    //Output the features
    IplImage* gabor1 = cvCreateImage(cvGetSize(img), img->depth, 1);
    IplImage* gabor2 = cvCreateImage(cvGetSize(img), img->depth, 1);
    for (int x = 0; x < img->width; x++)
        for (int y = 0; y < img->height; y++) {
            CV_IMAGE_ELEM(gabor1, uchar, y, x) = (obs[x][y](0));
            CV_IMAGE_ELEM(gabor2, uchar, y, x) = (obs[x][y](1));
            //std::cout << "x=" << x << " y=" << y << std::endl;
        }
    char buffer[50];
    sprintf(buffer,"gabor1.jpg");
    cvSaveImage(buffer, gabor1);
    cvReleaseImage(&gabor1);
    sprintf(buffer,"gabor2.jpg");
    cvSaveImage(buffer, gabor2);
    cvReleaseImage(&gabor2);
    
    
    //Init model
    ImageParameters::_nbFeature = obs[0][0].size().height; //3 = luv feature, 2 = uv features
    ImageParameters::_height = img->height;
    ImageParameters::_width = img->width;
    ImageParameters::_numberClass = 5; //We work with 5 classes
    ImageParameters::_beta = 2;
    GibbsParameters::_stop_criterion = 0.005;
    GibbsParameters::_initial_temperature = 5.0;
    GibbsParameters::_decreaseTemperature = 0.98;
    EMParameters::_maxIter = 10;
    EMParameters::_stop_criterion = 1e-7;

    mrf_t mrf;

    label_t** labels = new label_t*[ImageParameters::getWidth()];
    for (int i = 0; i < ImageParameters::getWidth(); ++i) {
        labels[i] = new label_t[ImageParameters::getHeight()];
    }
        
    //Segment the image
    IplImage* segmented = cvCreateImage(cvGetSize(img), img->depth, 1);
    mrf(obs, labels);
    //Output the result
    for (int x = 0; x < img->width; x++)
        for (int y = 0; y < img->height; y++) {
            CV_IMAGE_ELEM(segmented, uchar, y, x) = (255 * labels[x][y]) / ImageParameters::getL();
        }
    sprintf(buffer,"segmented n=%d B=%d.jpg",ImageParameters::getL(),(int)ImageParameters::getBeta());
    cvSaveImage(buffer, segmented);

    //Free
    for (int i = 0; i < ImageParameters::getHeight(); ++i) {
        delete[] obs[i];
    }
    delete[] obs;
    for (int i = 0; i < ImageParameters::getHeight(); ++i) {
        delete[] labels[i];
    }
    delete[] labels;

    cvReleaseImage(&img);
    cvReleaseImage(&segmented);

    return 0;
}

